#include "stdafx.h"
#include "MemoryBuffer.h"
#include "MemoryCopy.h" 

using namespace System;
using namespace Exception;

//Constructors
MemoryBuffer::MemoryBuffer(size_t capacity)
:m_size(0)
{
	m_capacity = capacity;
	m_data = new byte[m_capacity];
}

MemoryBuffer::MemoryBuffer(const byte* src, size_t size)
:m_size(size)
{
	m_data = new byte[size];
	MemoryCopy::copy((void*)m_data, (void*)src, size); 
}
MemoryBuffer::MemoryBuffer(const char* src)
{
	_ASSERTE(src != NULL);
	m_size = strlen(src);
	m_data = new byte[m_size];
	MemoryCopy::copy((void*)m_data, (void*)src, m_size); 
}

MemoryBuffer::MemoryBuffer(const MemoryBuffer& copy)
{
     m_size = copy.size();
	 m_capacity = m_size;
	 m_data = new byte[m_capacity];
	 MemoryCopy::copy(m_data, copy.getData(), m_size);
}

MemoryBuffer::~MemoryBuffer(void)
{
	if (m_data != NULL)
	{
		delete m_data;
		m_data = NULL;
	}
}
//operators
byte & MemoryBuffer::operator[](size_t index)
{
	if (m_capacity <= index)
	{
		throw IndexOutOfBoundsException();
		//return result;
	}
	if (index >= m_size)
	{
		m_size = index + 1;
	}

	return m_data[index];
}

byte MemoryBuffer::operator[](size_t index) const
{
	if (m_capacity <= m_size)
	{
		throw IndexOutOfBoundsException();
	}

	return m_data[index];
}

MemoryBuffer& MemoryBuffer::operator=(const MemoryBuffer &rhs) 
{
	if (this == &rhs)      
	  return *this;
    if (m_data != NULL)
	{
		delete m_data;
		m_data = NULL;
	}
	m_size = rhs.size();
	m_capacity = m_size;
	m_data = new byte[m_capacity];
	MemoryCopy::copy(m_data, rhs.getData(), m_size);
	return *this;
}
//API interface
size_t	MemoryBuffer::size(size_t offset) const
{
	return m_size - offset;
}

byte*	MemoryBuffer::getData(size_t offset ) const
{
	return m_data + offset;
}

void	MemoryBuffer::addData(const MemoryBuffer& buffer, size_t destOff , size_t srcOff )
{
	size_t offsetToAdd = destOff + buffer.size() - srcOff;
	if (offsetToAdd > 2* m_capacity)
		updateCapacity(offsetToAdd);
	else if (offsetToAdd > m_capacity)
		updateCapacity();

	MemoryCopy::copy(m_data, buffer.getData(destOff), buffer.size(), destOff , srcOff );
	updateSize(buffer.size(), destOff, srcOff);
}

void	MemoryBuffer::addData(byte* bytes, size_t bufSize, size_t destOff , size_t srcOff )
{
	size_t offsetToAdd = destOff + bufSize- srcOff;
	if (offsetToAdd > 2* m_capacity)
		updateCapacity(offsetToAdd);
	else if (offsetToAdd > m_capacity)
		updateCapacity();

	MemoryCopy::copy(m_data, bytes + srcOff, bufSize - srcOff, destOff , srcOff );
	
	updateSize(bufSize, destOff, srcOff);
}

void MemoryBuffer::updateCapacity()
{
	updateCapacity(m_capacity * 2);
}

void MemoryBuffer::updateCapacity(size_t newCap)
{
	if(m_capacity > newCap)
	{
		return;
	}

	m_data = new byte[newCap];
	m_capacity = newCap;
}

void MemoryBuffer::updateSize(size_t bufSize, size_t destOff , size_t srcOff )
{
	if (srcOff == 0)
	{
		m_size = bufSize - destOff;
		if (m_size > m_capacity)
		{
			updateCapacity(m_size);
		}
	}
	else
	{
		size_t newSize = srcOff + bufSize - destOff;
		if (m_size < newSize)
			m_size = newSize;
	}
}

bool	MemoryBuffer::startsWith (const MemoryBuffer& mem) const
{
	if (mem.getData() == NULL)
		return false;
	return !memcmp(m_data, mem.getData(), mem.size());
}

void MemoryBuffer::addData(LPCWSTR bytes, size_t bufSize, size_t destOff, size_t crtOff)
{
	//TODO - support unicode
	size_t origsize = wcslen(bytes) + 1;
	const size_t newsize = 266;
	size_t convertedChars = 0;
	char* copyBytes = new char[newsize];
	wcstombs_s(&convertedChars, copyBytes, origsize, bytes, _TRUNCATE);
	
	addData((byte*)copyBytes, bufSize, destOff, crtOff);

}
